# PicoPygments

A [PicoCMS][p] plugin that adds [Pygments][py] syntax highlighting to code blocks that have a language definition.

## Requirements

You need

- to have [PicoCMS][p] up and running.
- the DOM module for PHP (possibly also the SimpeXML and XML modules; on Debian, these are packaged into `php-xml`.
- Python3 and a Python3 version of [Pygments][py]. I tested with both `python3-pygments` from Debian stable repos (2.3.1) and a much newer,
  [pip][pip]-installed version - both worked.

Tested on Debian buster (stable), PicoCMS 2.1.4 and PHP 7.3.

## Installation

There are two ways - cloning the git repository, or manually downloading the plugin.

### Git clone

- Change into your PicoCMS install's `/plugin` directory, open a terminal there.
- Execute `git clone https://$site.org/ohnonot/PicoPygments`, where `$site` is either `framagit` or `notabug`.

If you follow a few simple rules, you can always `cd PicoPygments; git pull` to the newest version:

- Don't edit any files inside the repository.
- Copy the `picopygments.yml` config file to your PicoCMS install's `/config` directory.
- Create custom files inside the repository that have the word `custom` in their name (see `.gitignore`) and edit
  `/config/picopygments.yml` to point to those. Currently this only applies to the `stylesheet` configuration.

### Manual download

- Download the master.zip into your PicoCMS install's `/plugin` directory.
- Extract it in place - you should now have a `PicoPygments` folder that immediately contains `PicoPygments.php`
  and some other files. In other words, from your PicoCMS install's base directory, you should have
  `/plugins/PicoPygments/PicoPygments.php` (and some other files and directories).

That should be enough.

As usual, you should copy the `picopygments.yml` configuration file to your [PicoCMS][p] installation's `/config`
folder and make your adjustments there.

## Configuration

The plugin should be enabled by default, and its own caching should also work by default.  
If you want to make sure, set `debug: true` in your PicoCMS' `config.yml`, you will see some messages at the top
of the page. After first loading a page all code blocks on that page should be cached, greatly reducing loading times.
They are cached with a unique filename representing the code itself and PicoPygments' configuration.
If anything changes there, new versions are generated.

Additionally you can explicitely disable PicoPygments for a particular page by adding `picopygments: false` to its YAML header.

### Styles, Colors, CSS

#### Base16

If the page's already existing stylesheet declares the [CSS variables][cv] `--base00` through `--base0f` in its `:root`
(see the TagBlog theme's [README.md][tbr] ([mirror][tbrm]) for details), PicoPygments will automatically apply them.

This happens through the default `css/base16var_with_fallback.css` stylesheet. If
these variables are not defined, then the fallbacks following them will be applied . They come from the Base16 Equilibrium Light scheme (and the TagBlog theme currently uses the same as fallback).

As the names of the variables suggest they should adhere to the [Base16 Styling Guidelines][bsg].

However, it is still possible to use a different stylesheet with the `stylesheet` setting in `picopygments.yml`.
Options are provided in the `css/generated` folder.

BTW, you can preview many Pygments styles [here][sty], and most Base16 styles [here][bs].
Those Pygments styles that are not Base16 styles can be generated with `scripts/styles.py` (be sure to read `scripts/README.md` first).

Additional to colors, there's a `_common.css` file with overrides for all styles.

### Line numbers

It is possible by editing the `formatteroptions` configuration under the DANGER ZONE in `/config/picopygments.yml`.

I have tried to integrate line numbers, but this introduces too many quirks and (what I consider) inconsistencies in Pygments' behaviour.
They work fine as such, but possibly

- your code blocks will get even larger, with empty elements.
- the code block will look weird, with double borders etc (fixable with CSS).
- you have to decide if you want line numbers for either all code blocks or none.

## Usage

You have a markdown article with a fenced code block. If you append a language keyword to the opening fence,
this plugin will become active. Example:

~~~md
## This is what I coded in Lua:

```lua
-- try to find themes in $HOME
themedir = os.getenv("HOME") .. '/.local/share/themes/'
```
~~~

will use Lua syntax highlighting for this block of code.

If you're unsure which language to choose for your code you can run the included `guess.py` script,
enter the raw code in question, and Pygments will tell you what it thinks about it.

The plugin activates _after_ PicoCMS has converted your Markdown articles to HTML, it replaces code
blocks therein _if they have a language class_, like this: `<code class="language-lua">...</code>`.
[PicoCMS][p] uses Parsedown and [Parsedown Extra][pe] which is supposed to implement
[Markdown Extra][me], but I found that this particular feature is not as rich as [outlined here][mef]:

- It won't handle more than one class added.
- If you don't use the simple syntax above, but try to add multiple classes or dots or brackets
  (e.g. `~~~ .lua`, `~~~{.lua .my-code}`, `~~~{.lua #my-code}` etc.) the extra characters show up in
  the langauge class and you end up with something like `<code class="language-{.lua">`.

This plugin sanitizes these strings, but multiple classes won't work.

### Highlighting inline code (not a block of code)

Neither Markdown Extra nor Parsedown Extra have a means to put a CSS class on inline code, but you
can write it out as HTML in your Markdown text. Instead of

~~~
I typed `while true; do echo Yes; done` without looking.
~~~

you have to write

~~~
I typed <code class="language-bash">while true; do echo Yes; done</code> without looking.
~~~

## Background

The plugin does all its work server-side.  
If you care about keeping your site javascript free, you might like this.  
If not - I would recommend other solutions.

It is written in PHP, just like PicoCMS itself, but Pygments is written in Python and
<abbr title="A few wrappers exist, but they all call pygmentize via shell command, and add more needless overhead.">there is no PHP version of it</abbr>.  
Therefore it needs to execute an external program with exec, which is just `python3 -I` with an ad-hoc generated script
(it does __not__ call the `pygmentize` script included with python-pygmentize).  
This increases loading times much more than any pure PHP solution (but IMO Pygments is still the best,
most up-to-date solution for non-javascript syntax highlighting).

To minimise this impact the plugin

- considers only code blocks that are marked with a language class (it does not guess at the language)
- executes `python` _at most_ once per page (and not once per code block)
- caches Pygments' output and prefers to re-use that.

So the best-case scenario is that all code blocks have been cached peviously and will be read
from (RAM)disk, and there's no need to call python at all.  
I made some (unscientific, anecdotal) comparisons with Firefox' developer tools - the result is clear enough:

- Without internal caching this plugin adds between 66% and 91% to the loading time
  (the HTML of the page only, no assets like images or CSS).
- With internal caching (and all code blocks loaded from cache)
  the plugin adds between 2% and 5% to the loading time.

## Get it here

__[framagit.org/ohnonot/PicoPygments][fg] / [notabug.org/ohnonot/PicoPygments][nb]__

[p]: https://picocms.org/
[py]: https://pygments.org/
[pe]: https://github.com/erusev/parsedown-extra/
[me]: https://michelf.ca/projects/php-markdown/extra/
[mef]: https://michelf.ca/projects/php-markdown/extra/#fenced-code-blocks
[bfp]: https://github.com/mohd-akram/base16-pygments
[pip]: https://pypi.org/project/pip/
[sty]: https://dt.iki.fi/pygments-gallery
[cv]: https://developer.mozilla.org/en-US/docs/Web/CSS/var
[tbr]: https://notabug.org/ohnonot/tagblog/#automatic-styling
[tbrm]: https://framagit.org/ohnonot/tagblog/#automatic-styling
[bs]: https://dt.iki.fi/base16/previews
[fg]: https://framagit.org/ohnonot/PicoPygments
[nb]: https://notabug.org/ohnonot/PicoPygments